package edu.hrbu.kwaidu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.hrbu.kwaidu.exception.CustomException;
import edu.hrbu.kwaidu.mapper.SysUserMapper;
import edu.hrbu.kwaidu.model.entity.SysMenu;
import edu.hrbu.kwaidu.model.entity.SysRole;
import edu.hrbu.kwaidu.model.entity.SysRoleMenu;
import edu.hrbu.kwaidu.model.entity.SysUser;
import edu.hrbu.kwaidu.model.vo.LoginVo;
import edu.hrbu.kwaidu.model.vo.RouterVo;
import edu.hrbu.kwaidu.model.vo.SysUserQueryVo;
import edu.hrbu.kwaidu.service.SysMenuService;
import edu.hrbu.kwaidu.service.SysRoleMenuService;
import edu.hrbu.kwaidu.service.SysRoleService;
import edu.hrbu.kwaidu.service.SysUserService;
import edu.hrbu.kwaidu.util.CustomUtil;
import edu.hrbu.kwaidu.util.MenuUtil;
import edu.hrbu.kwaidu.util.RedisUtil;
import edu.hrbu.kwaidu.util.RouterUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static edu.hrbu.kwaidu.constant.SystemConstant.*;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author xudengyu
 * @since 2023-12-31
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private SysRoleService sysRoleService;

    @Resource
    private SysMenuService sysMenuService;

    @Resource
    private SysRoleMenuService sysRoleMenuService;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 条件分页查询
     *
     * @return pageParam
     */
    @Override
    public Page<SysUser> pageCondition(Integer page, Integer limit, SysUserQueryVo sysUserQueryVo) {
        Page<SysUser> pageParam = new Page<>(page, limit);
        LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 条件查询
        doProcessSelect(sysUserLambdaQueryWrapper, sysUserQueryVo);
        pageParam = page(pageParam, sysUserLambdaQueryWrapper);
        // 填充role属性
        pageParam.getRecords().forEach(sysUser -> {
            SysRole sysRole = sysRoleService.getInfoById(sysUser.getRoleId());
            sysUser.setSysRole(sysRole);
        });
        return pageParam;
    }

    /**
     * 处理条件查询
     *
     * @param sysUserLambdaQueryWrapper wrapper
     * @param sysUserQueryVo            vo
     */
    private static void doProcessSelect(LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper, SysUserQueryVo sysUserQueryVo) {
        // 不查询pwd
        doNeglectPwd(sysUserLambdaQueryWrapper);
        // 条件查询
        if (!BeanUtil.isEmpty(sysUserQueryVo)) {
            doProcessCondition(sysUserLambdaQueryWrapper, sysUserQueryVo);
        }
    }

    /**
     * 处理条件查询
     *
     * @param sysUserLambdaQueryWrapper wrapper
     * @param sysUserQueryVo            vo
     */
    private static void doProcessCondition(LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper, SysUserQueryVo sysUserQueryVo) {
        String name = sysUserQueryVo.getName();
        String phone = sysUserQueryVo.getPhone();
        Integer gender = sysUserQueryVo.getGender();
        Long roleId = sysUserQueryVo.getRoleId();
        Date createTimeBegin = sysUserQueryVo.getCreateTimeBegin();
        Date createTimeEnd = sysUserQueryVo.getCreateTimeEnd();
        if (StrUtil.isNotBlank(name))
            sysUserLambdaQueryWrapper.like(SysUser::getName, name);
        if (StrUtil.isNotBlank(phone))
            sysUserLambdaQueryWrapper.like(SysUser::getPhone, phone);
        if (gender != null)
            sysUserLambdaQueryWrapper.eq(SysUser::getGender, gender);
        if (roleId != null)
            sysUserLambdaQueryWrapper.eq(SysUser::getRoleId, roleId);
        if (createTimeBegin != null)
            sysUserLambdaQueryWrapper.ge(SysUser::getCreateTime, createTimeBegin);
        if (createTimeEnd != null)
            sysUserLambdaQueryWrapper.le(SysUser::getCreateTime, createTimeEnd);
    }

    /**
     * 不查询pwd
     *
     * @param sysUserLambdaQueryWrapper wrapper
     */
    private static void doNeglectPwd(LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper) {
        // 获取表中所有列表
        Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(SysUser.class);
        // 获取属性某一字段名
        String fieldName = LambdaUtil.getFieldName(SysUser::getPassword);
        // 脱敏 >> 不获取pwd
        sysUserLambdaQueryWrapper
                .select(SysUser.class,
                        col -> !col.getColumn()
                                .equals(columnMap
                                        .get(LambdaUtils.formatKey(fieldName))
                                        .getColumn()))
                .ne(SysUser::getRoleId, SYSTEM_ROOT_ID);
    }

    /**
     * 更改用户状态
     *
     * @param id     用户主键
     * @param status 状态：0-启用 1-禁用
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        checkInvalidModify(id);
        LambdaUpdateWrapper<SysUser> sysUserLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        sysUserLambdaUpdateWrapper.eq(SysUser::getId, id)
                .set(SysUser::getStatus, status);
        boolean f = update(sysUserLambdaUpdateWrapper);
        if (!f) throw new CustomException(EXCEPTION_UPDATE_FAIL);
    }

    /**
     * 检查是否操作管理员角色用户
     *
     * @param userId 用户主键
     */
    private void checkInvalidModify(Long userId) {
        SysUser sysUser = getById(userId);
        if (SYSTEM_ROOT_ID.equals(sysUser.getRoleId()))
            throw new CustomException(EXCEPTION_NO_AUTH);
    }

    /**
     * 删除用户
     *
     * @param id 用户主键
     */
    @Override
    public void del(Long id) {
        checkInvalidModify(id);
        removeById(id);
    }

    /**
     * 批量删除用户
     *
     * @param ids 主键列表
     */
    @Override
    @Transactional
    public void delBatch(ArrayList<Long> ids) {
        ids.forEach(this::del);
    }

    /**
     * 添加|修改
     *
     * @param sysUser 用户信息
     */
    @Override
    public void addOrEdit(SysUser sysUser) {
        checkUserBaseInfo(sysUser);
        boolean f = false;
        Long id = sysUser.getId();
        String name = sysUser.getName();
        String phone = sysUser.getPhone();
        Integer gender = sysUser.getGender();
        Long roleId = sysUser.getRoleId();
        SysUser dbUser = getUserByPhone(phone);
        checkRepeatUserPhone(sysUser, dbUser);
        if (id == null) { // add
            String defaultPwdMd5 = CustomUtil.getMd5Pwd(SYSTEM_DEFAULT_PWD);
            sysUser.setPassword(defaultPwdMd5);
            f = save(sysUser);
        } else { // edit
            LambdaUpdateWrapper<SysUser> sysUserLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            sysUserLambdaUpdateWrapper.eq(SysUser::getId, id)
                    .set(SysUser::getName, name)
                    .set(SysUser::getPhone, phone)
                    .set(SysUser::getGender, gender)
                    .set(SysUser::getRoleId, roleId);
            f = update(sysUserLambdaUpdateWrapper);
        }
        if (!f) {
            throw new CustomException(EXCEPTION_UPDATE_FAIL);
        }
    }

    /**
     * 检查手机号是否已被使用
     *
     * @param originUser 待修改的user
     * @param dbUser     库中的user
     */
    private static void checkRepeatUserPhone(SysUser originUser, SysUser dbUser) {
        if (dbUser == null)
            return;
        if (originUser.getId() == null)
            throw new CustomException(EXCEPTION_USER_SAVE_TEL_REPEAT);
        if (!originUser.getId().equals(dbUser.getId()))
            throw new CustomException(EXCEPTION_USER_SAVE_TEL_REPEAT);
    }

    /**
     * phone >> sysUser
     *
     * @param phone 手机号[登录凭证 唯一]
     * @return sysUser
     */
    @Override
    public SysUser getUserByPhone(String phone) {
        LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sysUserLambdaQueryWrapper.eq(SysUser::getPhone, phone);
        return getOne(sysUserLambdaQueryWrapper);
    }

    /**
     * 检查sysRole基本信息
     *
     * @param sysUser 系统用户
     */
    private void checkUserBaseInfo(SysUser sysUser) {
        Long id = sysUser.getId();
        String name = sysUser.getName();
        String phone = sysUser.getPhone();
        Integer gender = sysUser.getGender();
        Long roleId = sysUser.getRoleId();
        if (id != null)
            checkInvalidModify(id);
        if (StrUtil.hasBlank(name, phone) && gender != null && roleId != null)
            throw new CustomException(EXCEPTION_USER_INFO_BLANK);
        if (SYSTEM_ROOT_ID.equals(roleId))
            throw new CustomException(EXCEPTION_NO_AUTH);
        if (phone.length() != SYSTEM_TEL_LENGTH)
            throw new CustomException(EXCEPTION_USER_INFO_INVALID);
    }

    /**
     * 通过主键获取基本信息
     *
     * @param id 用户主键
     * @return sysUser
     */
    @Override
    public SysUser getInfoById(Long id) {
        SysUser userDb = getById(id);
        return doGetSafetyUser(userDb);
    }

    /**
     * 用户信息脱敏
     *
     * @param originalUser 未脱敏用户
     * @return safetyUser 脱敏用户
     */
    private SysUser doGetSafetyUser(SysUser originalUser) {
        SysUser sysUser = new SysUser();
        sysUser.setId(originalUser.getId());
        sysUser.setName(originalUser.getName());
        sysUser.setPhone(originalUser.getPhone());
        sysUser.setGender(originalUser.getGender());
        sysUser.setStatus(originalUser.getStatus());
        sysUser.setCreateTime(originalUser.getCreateTime());
        sysUser.setUpdateTime(originalUser.getUpdateTime());
        sysUser.setRoleId(originalUser.getRoleId());
        sysUser.setIsDeleted(originalUser.getIsDeleted());
        return sysUser;
    }

    /**
     * 用户登录
     *
     * @param loginVo 登录信息
     * @return map-token
     */
    @Override
    public HashMap<String, String> login(LoginVo loginVo) {
        // 判断信息是否为空
        if (BeanUtil.isEmpty(loginVo)) throw new CustomException(EXCEPTION_USER_INFO_BLANK);
        // 判断信息是否合法
        String password = loginVo.getPassword();
        String phone = loginVo.getPhone();
        if (phone.length() != SYSTEM_TEL_LENGTH
                || password.length() > SYSTEM_PWD_MAX_LENGTH
                || password.length() < SYSTEM_PWD_MIN_LENGTH) throw new CustomException(EXCEPTION_USER_INFO_INVALID);
        // 查库，看手机号是否注册
        SysUser userDb = getUserByPhone(phone);
        if (userDb == null) throw new CustomException(EXCEPTION_USER_LOGIN_TEL_INEXIST);
        // 判断状态是否启用
        if (!SYSTEM_ENABLE_STATUS.equals(userDb.getStatus())) throw new CustomException(EXCEPTION_DISABLE);
        // 比对密码
        String pwdDb = userDb.getPassword();
        if (!pwdDb.equals(CustomUtil.getMd5Pwd(password))) throw new CustomException(EXCEPTION_USER_LOGIN_FAIL);
        HashMap<String, String> map = new HashMap<>();
        String token = CustomUtil.getEncodeToken(phone);
        // 存储token
        map.put(REQ_HEADER_TOKEN, token);
        // set redis >> token:userInfo
        redisUtil.setCurrentUserCache(token, userDb);
        return map;
    }

    public static final String REQ_STORE_ROLES = "roles";
    public static final String REQ_STORE_NAME = "name";
    public static final String REQ_STORE_PHONE = "phone";
    public static final String REQ_STORE_INTROD = "introduction";
    public static final String REQ_STORE_AVATOR = "avatar";
    public static final String REQ_STORE_ROUTERS = "routers";
    public static final String REQ_STORE_BUTTONS = "buttons";
    public static final String DEFAULT_ROLE = "[admin]";

    /**
     * 通过token获取用户信息
     *
     * @param token Base64(phone)
     * @return hashmap
     */
    @Override
    public HashMap<String, Object> getInfoByToken(String token) {
        if (StrUtil.isBlank(token)) throw new CustomException(EXCEPTION_TOKEN_BLANK);
        String phone = CustomUtil.getDecodeToken(token);
        // 通过token获取信息
        HashMap<String, Object> map = new HashMap<>();
        // 通过phone获取用户信息
        SysUser sysUser = getUserByPhone(phone);
        Long userId = sysUser.getId();
        Long roleId = sysUser.getRoleId();
        sysUser.setSysRole(sysRoleService.getById(roleId));
//        System.out.println("@>> " + sysUser);
        if (BeanUtil.isEmpty(sysUser)) throw new CustomException(EXCEPTION_TOKEN_BLANK);
        // 通过userId获取菜单列表
        List<RouterVo> routerList = getRouterListByUserId(userId);
        // 通过userId获取按钮列表
        List<String> permsList = getPermsListByUserId(userId);
        redisUtil.setPermsCache(token, permsList);

        map.put(REQ_STORE_ROLES, DEFAULT_ROLE);
        map.put(REQ_STORE_NAME, sysUser.getName());
        map.put(REQ_STORE_PHONE, phone);
        map.put(REQ_STORE_ROUTERS, routerList);
        map.put(REQ_STORE_BUTTONS, permsList);
        return map;
    }

    /**
     * 通过roleId获取对应menu
     *
     * @param sysMenuList 菜单列表
     * @param roleId      角色主键
     */
    private List<SysMenu> menuListCondition(List<SysMenu> sysMenuList, Long roleId) {
        if (SYSTEM_ROOT_ID.equals(roleId)) { // ROOT角色 >> 拥有所有权限
            LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 启用状态
            sysMenuLambdaQueryWrapper.eq(SysMenu::getStatus, SYSTEM_ENABLE_STATUS);
            sysMenuLambdaQueryWrapper.orderByAsc(SysMenu::getSortValue);
            sysMenuList = sysMenuService.list(sysMenuLambdaQueryWrapper);
        } else { // 非ROOT
            buildMenuListByRoleId(sysMenuList, roleId);
        }
        return sysMenuList;
    }

    /**
     * 通过userId获取routerList[sidebar]
     *
     * @param userId 用户主键
     * @return routerList
     */
    @Override
    public List<RouterVo> getRouterListByUserId(Long userId) {
        List<SysMenu> sysMenuList = new ArrayList<>();

        // 获取roleId
        SysUser sysUser = getById(userId);
        Long roleId = sysUser.getRoleId();
        sysMenuList = menuListCondition(sysMenuList, roleId);
        List<SysMenu> sysMenuTreeList = MenuUtil.buildTree(sysMenuList);
        //构建成框架要求的路由结构
        List<RouterVo> routerList = RouterUtil.buildRouter(sysMenuTreeList);
        return routerList;
    }

    private void buildMenuListByRoleId(List<SysMenu> sysMenuList, Long roleId) {
        // [roleMenu] roleId >> menuIdList
        LambdaQueryWrapper<SysRoleMenu> sysRoleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sysRoleMenuLambdaQueryWrapper.eq(SysRoleMenu::getRoleId, roleId);
        List<Long> menuIdList = sysRoleMenuService.list(sysRoleMenuLambdaQueryWrapper).stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
        // [menu] menuIdList->menuList
        for (Long menuId : menuIdList) {
            sysMenuList.add(sysMenuService.getById(menuId));
        }
    }

    /**
     * 通过userId获取按钮列表
     *
     * @param userId 用户主键
     * @return permsList
     */
    @Override
    public List<String> getPermsListByUserId(Long userId) {
        List<SysMenu> sysMenuList = new ArrayList<>();

        // 获取roleId
        SysUser sysUser = getById(userId);
        Long roleId = sysUser.getRoleId();
        sysMenuList = menuListCondition(sysMenuList, roleId);

        List<String> permsList = sysMenuList.stream()
                .filter(i -> SYSTEM_BUTTON_TYPE.equals(i.getType()))
                .map(SysMenu::getPerms)
                .collect(Collectors.toList());

        return permsList;
    }

    /**
     * 退出登录
     */
    @Override
    public void logout(String token) {
        redisUtil.removeCurrentUserCache(token);
        redisUtil.removePermsCache(token);
    }
}
